
%% Author: f355
%% Created: 22.02.2010
%% Description: TODO: Add description to ddc_mnesia
-module(ddc_db).

%%
%% Include files
%%
-include("ddc_records.hrl").

%%
%% Exported Functions
%%
-export([read_random/3]).
-compile(export_all).

%%
%% API Functions
%%

start() ->
    case mnesia:system_info(extra_db_nodes) of
	[] ->
	    mnesia:create_schema([node()]);
	_ ->
	    ok
    end,
    application:start(mnesia, permanent).

stop() ->
    application:stop(mnesia).

%% Initialize database, creating schema and tables. One-timer, used during installation only.
init_db() ->
    mnesia:create_table(phrases, [{attributes, record_info(fields, tuple)}, {disc_copies, [node()]}]),
    mnesia:create_table(words, [{attributes, record_info(fields, tuple)}, {disc_copies, [node()]}]),
    mnesia:create_table(learn_log, [{attributes, record_info(fields, learn_log)}, {disc_only_copies, [node()]}]),
    mnesia:wait_for_tables([phrases, words, learn_log], infinity),
    ok.

%% DANGEROUS! Clears entire database, except the learning log.
nuke(no_log) ->
    mnesia:clear_table(phrases),
    mnesia:clear_table(words);

nuke(with_log) ->
    nuke(no_log),
    mnesia:clear_table(learn_log).

%% Runs query (defined as fun()) in transaction-driven or dirty mode.
run_query(Query, Mode)->
    case Mode of
	safe ->
	    mnesia:transaction(Query);
	dirty ->
	    mnesia:sync_dirty(Query);
	dirty_spawn ->
	    spawn(mnesia, sync_dirty, [Query]),
	    spawned
    end.

%% Writes learning log entry.
write_log(String) ->
    mnesia:write(learn_log, #learn_log{sentence=String, void=void}, write).

%% Evaluates Fun/1 against each entry in learning log.
%% Returns {ok, Num}, where Num is log entries count. 
%% Used for dumping log to file and log replay.
%% WARNING! Iteration is dirty. So if someone tries to write something
%% to log table during iteration, then behavior is unpredicted.
iterate_log(Fun) ->
    iterate_log(Fun, start, 0).

iterate_log(Fun, Rec, Num) ->
    case Rec of
	start ->
	    iterate_log(Fun, mnesia:dirty_first(learn_log), 0);
	'$end_of_table' ->
	    {ok, Num};
	R ->
	    %%mnesia:wait_for_tables([words, phrases], infinity),
	    io:format("~w ", [Num]),
	    case Fun(R) of
		ok -> iterate_log(Fun, mnesia:dirty_next(learn_log, R), Num+1);
		Error -> Error
	    end
    end.

%% Writes data to specified table, merging it with existing.
write(Table, List) ->
    lists:foreach(
      fun(T) ->
	      T_old=mnesia:read(Table, T#tuple.key),
	      T_new=case T_old of
			[{Table, Key, Old_f, Old_b}] ->
			    {Table, Key, sets:union(Old_f, T#tuple.vals_f), sets:union(Old_b, T#tuple.vals_b)};
			_ ->
			    {Table, T#tuple.key, T#tuple.vals_f, T#tuple.vals_b}
		    end,

	      mnesia:write(Table, T_new, write)
      end, List).

read_random(Table, Key, Dir) ->
    case read_list(Table, Key, Dir) of
	{ok, Val} -> {ok, lists:nth(random:uniform(length(Val)), Val)};
	Other -> Other
    end.

random_key(Table) ->
    Keys = mnesia:all_keys(Table),
    case length(Keys) of
	0 -> {error, no_data};
	_ -> {ok, lists:nth(random:uniform(length(Keys)), Keys)}
    end.

read_set(Table, Key, Dir) ->
    case mnesia:read(Table, Key) of
	[{Table, Key, Vals_f, Vals_b}] ->
	    Res=case Dir of
		    f->Vals_f;
		    b->Vals_b;
		    _-> {error, wrong_dir}
		end,
	    Is_noresult=(sets:is_set(Res) andalso sets:size(Res)=:=0),
	    if
		Is_noresult -> noresult;
		true -> {ok, Res}
	    end;
	[] -> nokey;
	Error -> Error
    end.

read_list(Table, Key, Dir) ->
    Res=read_set(Table, Key, Dir),
    case Res of
	{ok, Val} -> {ok, sets:to_list(Val)};
	Other -> Other
    end.

